/*********************************************************************
 *    Project Name : trust-uuc-service
 *    Package Name : com.trust.smart.uuc.filter
 *    Create  Time : 2021/3/25 10:09
 *    =============================================================
 *    (C) Copyright Trustcro Information Technology Co., Ltd.
 *          Corporation 2018-2021 All Rights Reserved.
 *
 *	  【注意： 本内容仅限于长沙信立诚医药科技有限公司内部使用，禁止转发】
 **********************************************************************/
package com.trust.pams.filter;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.chasonx.common.config.JWTRet;
import com.chasonx.common.config.JWTUtil;
import com.chasonx.common.config.RedisTemplateUtils;
import com.chasonx.common.filter.BaseSessionFilter;
import com.chasonx.common.filter.WebRouterProperties;
import com.chasonx.common.util.ConstantCode;
import com.chasonx.common.util.Ret;
import com.trust.smart.uuc.model.vo.TokenCheckRet;
import com.trust.smart.uuc.service.provider.ILoginProvider;
import com.trust.smart.uuc.util.TokenStatus;

import io.jsonwebtoken.Claims;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * @Project : trust-uuc-service
 * @Author : Chasonx<zc.x>
 * @CreateTime : 2021/3/25 10:09
 * @Desc :
 * @Version : 1.0
 */
public class SessionFilter extends BaseSessionFilter {

    private static final Logger log = LoggerFactory.getLogger(SessionFilter.class);

    @Reference(timeout = 60000 )
    ILoginProvider loginProvider;
    
    private WebRouterProperties webRouterProperties;
    private static Map<String , Map> excludeDFA = null;
    private RedisTemplateUtils redisTemplateUtils;

    private static final String HEADER_AUTHOR = "Authorization";

    private enum  SessionResult{
        /**
         * 无权限
         */
        unauthorized,
        /**
         * 过期
         */
        expiration
    }

    public SessionFilter(){}

    public SessionFilter(WebRouterProperties webRouterProperties){
        this.webRouterProperties = webRouterProperties;
        initExcludeDFA();
    }

    public void setRedisTemplateUtils(RedisTemplateUtils redisTemplateUtils){
        this.redisTemplateUtils = redisTemplateUtils;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest requestServlet, ServletResponse responseServlet, FilterChain chain){
        HttpServletRequest request = (HttpServletRequest) requestServlet;
        HttpServletResponse response = (HttpServletResponse) responseServlet;
        String uri = request.getRequestURI();
        uri = uri.substring(uri.substring(1).indexOf("/") + 1);
        try {
            log.info("requestUri : {}" , uri);
            if (isExcludePath(uri)) {
                chain.doFilter(request, response);
            } else {
                String authToken = request.getHeader(HEADER_AUTHOR);
                if (!StringUtils.hasText(authToken)) {
                    renderSessionFaild(SessionResult.unauthorized, response);
                    return;
                }
                TokenCheckRet rets=   loginProvider.validityCheckTokenByCache(authToken);
                if(TokenStatus.Exception ==rets.getStatus()) {
                	  renderSessionFaild(SessionResult.expiration, response);
                      return;
                }else if(TokenStatus.Success ==rets.getStatus()) {
                	 renderSessionFaild(SessionResult.unauthorized, response);
                     return;
                }
                JWTRet ret = JWTUtil.parseJWT(authToken);
                if( JWTRet.Status.Expiration.equals(ret.getStatus())){
                    renderSessionFaild(SessionResult.expiration, response);
                    return;
                }else if (!JWTRet.Status.Success.equals(ret.getStatus())) {
                    renderSessionFaild(SessionResult.unauthorized, response);
                    return;
                }
                
                /*Claims claims = ret.getClaims();

                log.info(" current token key : {}", claims.getId());
                
                 * publicService USER
                 
                if(!claims.containsKey("type") || !ConsCode.USER_TYPE_PUBLIC_SERVICE.equals(claims.get("type"))){
                	
                     * publicService USER
                     
                    Object cacheToken = redisTemplateUtils.get(claims.getId());
                    if (null == cacheToken) {
                        renderSessionFaild(SessionResult.expiration, response);
                        log.info("current token key : {} is expiration", claims.getId());
                        return;
                    }

                    log.info("current user name : {}", cacheToken);
                    CacheUtil.refreshUserToken(claims.getId());
                }else{
                    request.getSession().setAttribute(ConsCode.JWT_LOGIN_USERINFO, claims.get(ConsCode.JWT_LOGIN_USERINFO));
                    chain.doFilter(request, response);
                    return;
                }

                request.getSession().setAttribute(ConsCode.JWT_LOGIN_USERINFO, claims.get(ConsCode.JWT_LOGIN_USERINFO));
                
                 //MenuPermission Validate
                 
                if(!PermissionValidate.isLegalRouter(uri)){
                    log.info("{} requiestUrl : {} -> noForbidden." , claims.getId() , uri);
                    renderForbidden(response);
                    return;
                }*/
                chain.doFilter(request, response);
            }
        }catch (IOException | ServletException e){
            log.error("sessionFilter exception => " ,e);
        }
    }

    private void renderSessionFaild(SessionResult result, HttpServletResponse response) throws IOException {
        response.getWriter().write("{ \"result\" : \""+ result.name() +"\" }");
    }

    private void renderError(HttpServletResponse response) throws IOException {
        response.getWriter().write(JSON.toJSONString(Ret.get().error()));
    }

    private void renderForbidden(HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(JSON.toJSONString(Ret.get().faild(ConstantCode.StatusCode.A0403)));
    }

    @Override
    public void destroy() {
    }

    public void initExcludeDFA(){
        if(null == webRouterProperties){
            return;
        }
        if( excludeDFA == null){
            excludeDFA = new HashMap<>(16);
        }

        List<String> excludePaths = webRouterProperties.getExcludePath();
        excludePaths.stream().forEach(r -> {
            List<String> listUri = splitToList(r);

            Map<String,Map> childMap = null;
            for(int i = 0,len = listUri.size();i < len;i++){
                if(!excludeDFA.containsKey( listUri.get(i)) && i == 0){
                    childMap = new HashMap<>();
                    excludeDFA.put( listUri.get(i) , childMap);
                }else if(i == 0){
                    childMap = excludeDFA.get( listUri.get(i));
                }else if( !childMap.containsKey( listUri.get(i)) ){
                    childMap.put( listUri.get(i) , new HashMap<>());
                    childMap = childMap.get( listUri.get(i));
                }else{
                    childMap = childMap.get( listUri.get(i));
                }
            }

        });
    }

    public boolean isExcludePath(String uri){
        boolean result = false;
        if(null == uri){
            return result;
        }

        List<String> uriList = splitToList( uri );
        Map<String,Map> excludeMap = excludeDFA;
        String uriSegment;

        int len = uriList.size();
        int end = len - 1;
        for(int i = 0; i < len;i++){
            uriSegment = uriList.get(i);

            if( i == end ){
                Optional<String> on = checkPattern(excludeMap.keySet());
                if(on.isPresent() ){
                    result = true;
                    break;
                }else if( excludeMap.containsKey( uriSegment )){
                    result = true;
                    break;
                }
            }else {
                if (excludeMap.containsKey(uriSegment)) {
                    excludeMap = excludeMap.get(uriSegment);
                } else {
                    Optional<String> on = checkPattern(excludeMap.keySet());
                    if (on.isPresent()) {
                        excludeMap = excludeMap.get(on.get());
                        if (excludeMap.isEmpty()) {
                            result = true;
                            break;
                        }
                    } else {
                        break;
                    }
                }
            }
        }
        return result;
    }

    public Optional<String> checkPattern(Set<String> keySet){
        Optional<String> on = keySet.stream().filter(s -> s.equals("*") || s.equals("**")).findFirst();
        return on;
    }

    public List<String> splitToList(String uri){
        List<String> uriSplits = new ArrayList<>();

        while(true){
            int idx = uri.indexOf("/");
            if(idx < 0){
                uriSplits.add( uri );
                break;
            }
            if(idx > 0) {
                uriSplits.add(uri.substring(0, idx));
            }
            uri = uri.substring(idx + 1);
        }
        return uriSplits;
    }
}
